#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
VERSION = '0.0'
#
# Version history: 0.0		: 02-02-2010 : created by Vasco Rio using Python 2.6.4 and Pygame 1.8.1
#
# Programming by Vasco Rio (vasco.rio@gmail.com); License: GPL v.3
#
# Look for the latest version on: http://code.google.com/p/cleansim/
#
#####################################################################

# separar sortGene, greedy

from threading import Thread
from code import InteractiveConsole
import sys

from simData import *



from math import *
from random import *
import os.path
import pygame
from pygame.locals import *
import time
import pickle


#sprites
from robot import *
from building import *
from dirt import *
from floor import *
from gameUtils import *
from label import *
from taskManager import *
from auctions import *

#aux classes
from timer import *
from consoleClear import *


SCREENRECT=Rect(0, 0, 100, 100) 
SCREENRECT_fs = Rect(0, 0, 1280, 800) 
#====================================== IMPORT VALIDATION =========================================#

#see if we can load more than standard BMP
if not pygame.image.get_extended():
	raise SystemExit, "Sorry, extended image module required"



#============================================= MAIN ===============================================#
class CleanSim(Thread):
	ZOOM = 0.6
	global ONLINE
	global SIMSPEED
	global BLOCKS_MATRIX
	def __init__(self):
		print "Starting Clean Robot Simulator..."
		
		# Import Psyco if available
		try:
			import psyco
			psyco.full()
			psyco.log()
			psyco.profile(0.05)
		except ImportError:
			pass

		self.SIMSPEED = SIMSPEED
		pygame.init()
		self.clock = pygame.time.Clock()
		Thread.__init__(self)
		
		self.globalTime=0.0
		self.incTime=0.25
		self.daysCount=0
		
		self.robot_sim_clock=Clock(1./CONTROL_RATE,self.robot_sim)
		self.robot_imagination_clock=Clock(1./IMAGINATION_RATE,self.robot_imagination)
		
		fullscreen=False
		if fullscreen:
			bestdepth = pygame.display.mode_ok(SCREENRECT_fs.size, pygame.FULLSCREEN, 32)
			self.screen = pygame.display.set_mode(SCREENRECT_fs.size, pygame.FULLSCREEN, bestdepth)
		else:
			self.screen = pygame.display.set_mode(SCREENRECT.size,pygame.RESIZABLE)

		# Carregar SKIN
		Robot.images=[load_transparency('cleanRob_v0.2.png'),load_transparency('robot2_v0.1.png')]
		Robot.hidden_img=load_image('hidden_img.png')
		Floor.hidden_img=load_image('hidden_img.png')
		Dirt.hidden_img=load_image('hidden_img.png')
		DebugSprite.hidden_img=load_image('hidden_img.png')
		
		if DEBUG:
			debug=" - debug mode"
		else:
			debug = ""
		pygame.display.set_caption('CleanSim ' + VERSION + debug)
		#pygame.mouse.set_visible(0)

		# Create the background: tile the bgd image
		#bgdtile = load_background('chao_fabril.bmp', None)#'Arcade_tanks_background.jpg', None)
		
		
		self.background = pygame.Surface(self.screen.get_size())
		self.background = self.background.convert()
		self.background.fill((250, 250, 250))
		
		pygame.display.flip()

		# Initialize Game Groups
		self.labels = pygame.sprite.Group()
		self.robots = pygame.sprite.Group()
		self.dirts = pygame.sprite.Group()
		self.debugsprites = pygame.sprite.Group()
		self.floors = pygame.sprite.Group()
		
		#actualiza a imagem dos sprites por ordem de adição
		self.all = pygame.sprite.OrderedUpdates()

		#~ self.all = pygame.sprite.OrderedUpdates()
		#~ self.all = pygame.sprite.LayeredUpdates()
		#all = pygame.sprite.RenderUpdates()
		"""lastalien = pygame.sprite.GroupSingle()"""

		# Assign default groups to each sprite class
		Label.containers = self.labels, self.all
		Robot.containers = self.robots, self.all
		Dirt.containers = self.dirts, self.all
		DebugSprite.containers = self.debugsprites, self.all
		Floor.containers = self.floors, self.all
		
		# Limpeza geral
		self.all.clear(self.screen, self.background)
		
		self.spriteKillAll()

		# Construção do mapa
		import sys
		
		if len(sys.argv)>2:
			floornum=int(sys.argv[0+1])
			robots=int(sys.argv[1+1])
			self.go=sys.argv[2+1].lower()=='go'
		else:
			floornum=1
			robots=1
			self.go=True
		
		print "robots =",robots, "; floornum =",floornum
		
		self.building=Building(self,activeFloor=floornum)
		#self.building.addFloor('small_room_map', 0, scale = 40.0)	#piso0, 20px/m
		#self.building.addFloor('small_room_map2', 1, scale = 10.0)	#piso1
		if floornum==1:
			self.building.addFloor('feup_piso2', floornum, scale = 24.8/1140*1236)	#piso1
		elif floornum==-1:
			self.building.addFloor('feup_piso-1', floornum, scale = 24.8)	#piso1
		else:
			self.building.addFloor('feup_piso0', floornum, scale = 24.8*725/1140)	#piso1
		self.resize(self.zoomed())
		
		
		self.robotlist=[]
		
		def factors(n):
			for x in xrange(2,n):
				if n%x == 0:
					return (x,) + factors(n/x)
			return (n,1)
		
		f = factors(robots)
		x_elem = f[:len(f)/2]
		y_elem = f[-len(f)/2:]
		
		x_num = 1
		y_num = 1
		
		for i in x_elem:
			x_num*=i
		
		for i in y_elem:
			y_num*=i
			
		for x in range(x_num):
			for y in range(y_num):
				if floornum==1:
					robot=Robot(self,34.0+x,12.8+y,0.0,400.0,floornum,CLEANSIM_MODEL)
					robot.node0=self.building.getNodeByName('r7')
				
				elif floornum == -1:
					robot=Robot(self,10.0+x,3.0+y,0.0,400.0,floornum,CLEANSIM_MODEL)
					robot.node0=self.building.getNodeByName('r28')
				else:
					robot=Robot(self,8.0+x,1.0+y,0.0,400.0,floornum,CLEANSIM_MODEL)
					robot.node0=self.building.getNodeByName('r47')
				self.robotlist.append(robot)
		
		self.robot=self.robotlist
		
		#Allias
		self.fl=self.building.floors
		
		#~ self.g=GlobalCleanTask(self.robot,self.building)
		self.auction=Auction(self.robot,self.building,False,rand=False)
		
		self.loadSprites()
		

	def walk(self,to=None,endInDoor=False,robot=None):
		nodes=self.floor.rooms+self.floor.doors
		
		if(robot ==None):
			robot=self.robot[0]
		
		try:
			robot.node0
		except AttributeError:
			robot.node0=sample(nodes,1)[0]
			while(not robot.node0.connected):robot.node0=sample(nodes,1)[0]
		if to==None:
			node1=sample(nodes,1)[0]
		else:
			node1=self.building.getNodeByName(to)
		
		if node1!=None and node1!=robot.node0:
			while(not node1.connected): node1=sample(nodes,1)[0]
			
			nodes=self.building.path.shortestPath(robot.node0,node1)
			if (endInDoor) and isinstance(node1,Room):
				nodes.pop(-1)
			poss=[]
			for i in range(len(nodes)-1):
				if isinstance(nodes[i],Room):
					room=nodes[i]
					door=nodes[i+1]
					#correcção do bug na posição inicial
					if room is robot.node0:
						pos=robot.pos
					else:
						pos=room.pos
					poss+=room.area.findPath(pos,door.pos)
				elif isinstance(nodes[i+1],Room):
					door=nodes[i]
					room=nodes[i+1]
					poss+=room.area.findPath(door.pos,room.pos)
				else:
					door1=nodes[i]
					door2=nodes[i+1]
					room=self.building.path.findConnectiongRoom(door1,door2)
					if room!=None:
						poss+=room.area.findPath(door1.pos,door2.pos)
			
			name='Deslocação de '+str(robot.node0)+' para '+str(node1)+'.'
			robot.addOrder(name,poss)
			
			#robot.addt(poss)
			
			robot.node0=node1
	
	
	def rcAll(self):
		for room in self.rooms:
			self.randClean(room.name)
	
	def gaAll(self):
		for room in self.rooms:
			self.clean(room.name)
	
	def randClean(self,to=None):
		self.walk(to,endInDoor=True)
		
		for robot in self.robot:
			if isinstance(robot.node0,Room):
				areas=robot.node0.areas.values()#.cellDecomp().values()
				poss=[]
				for area in areas:
					ordSPos=area.getPositionsOrdS()
					
					if(len(poss)==0):
						try:
							pathToArea=robot.node0.area.findPath(robot.targets[len(robot.targets)-1][1],ordSPos[0])
						except:
							pathToArea=robot.node0.area.findPath(robot.pos,ordSPos[0])
					else:
						pathToArea=robot.node0.area.findPath(poss[len(poss)-1],ordSPos[0])
					poss+=pathToArea
					poss+=ordSPos
					
					#robot.addt(robot.node0.area.findPath(robot.targets[len(robot.targets)-1][1],ordSPos[0]))
					#robot.addt(ordSPos)
				#robot.addt(robot.node0.area.getPositionsOrdS())
				name='Limpeza do compartimento '+str(robot.node0)+' com randClean.' 
				robot.addOrder(name,poss)
	
	def clean(self,to=None,robot=None):
		self.walk(to,endInDoor=True)
		if robot == None:
			robot=self.robot[0]
			
		if isinstance(robot.node0,Room):
			poss=[]
			
			ordPos=robot.node0.winnerPath()#searchAC.bestPath()
			
			pathToArea=robot.node0.area.findPath(robot.last_target,ordPos[0])
				
			poss+=pathToArea
			poss+=ordPos
				
			name='Limpeza do compartimento '+str(robot.node0)+' Com genoma='+str(robot.node0.winner.chromosome)#searchAC.winner.chromosome) 
			robot.addOrder(name,poss)
		
	def spriteKillAll(self):
		u"""Remove all sprites"""
		
		pass
		#for kiva in self.kivas:
		#	kiva.kill()
	
	def resize(self,size):
		screen = pygame.display.set_mode(size, pygame.RESIZABLE)#event.size gives the size of the resized window as a rect
		self.background = pygame.Surface(self.screen.get_size())
		self.background = self.background.convert()
		self.background.fill((250, 250, 250))
	
	def loadSprites(self):
		"""Load all the sprites"""
		pass
		#self.all = pygame.sprite.OrderedUpdates(
		#	self.pms,self.red_kiva_floor,self.blue_kiva_floor,self.green_kiva_floor,self.brown_kiva_floor,self.floors)
			
			
	@property
	def floor(self):
		return self.building.visibleFloor
	
	@property
	def rooms(self):
		return self.floor.rooms
	
	def changeVisibleFloor(self,newActiveFloor):
		if newActiveFloor in self.building.floors.keys():
			self.building.activeFloor=newActiveFloor
			
			self.resize(self.zoomed())
			print "Current floor: "+str(newActiveFloor)
			
	def zoomed(self):
		size=self.building.floors[self.building.activeFloor].surface.get_size()
		scale=self.building.floors[self.building.activeFloor].scale
		
		return (int(size[0]*self.ZOOM/scale*PIXEL2M),int(size[1]*self.ZOOM/scale*PIXEL2M))

		
	def robot_sim(self):
		self.globalTime+=self.incTime
		
		if abs(self.globalTime%int(DIRT_SECONDS))<self.incTime/2:
			if not FASTMODE:
				self.floor.dirt.redirt(DIRT_PERCENT)
		
		if self.globalTime%(24*3600)<self.incTime/2:
			self.daysCount+=1
			print 'day',self.daysCount, 'is over.'
		for robot in self.robot:
			robot.update()
		
		if self.go:
			self.auction.execute(self.globalTime)


	def robot_imagination(self):
		#~ print time.time()
		no_robot_imagining=True
		#~ print 'entrou'
		for room in self.rooms:
			try:
				room.robot
				isdef=True
			except:
				pass
				isdef=False
				
			if isdef:
				room.robot.update()
				#~ print room,'está a imaginar'
				no_robot_imagining=False

		if no_robot_imagining:
			#~ print 'oi'
			self.robot_imagination_clock.runTime=1.
		else:
			self.robot_imagination_clock.runTime=1./IMAGINATION_RATE

	def getLarguraPX(self):
		return self.screen.get_width()
	def getAlturaPx(self):
		return self.screen.get_height()
		
	def getPixelInSreen(self,pos):
		if isinstance(pos,Position):
			x=pos.x
			y=pos.y
		elif isinstance(pos,tuple):
			x=pos[0]
			y=pos[1]
			
		ymax=self.getAlturaPx()
		return (int(x*PIXEL2M*self.ZOOM),int(ymax-y*PIXEL2M*self.ZOOM))
	def run(self):
		"""Inicio da do ciclo do Simulador Kiva"""

		print "Type dir(c) to console interactions."
		print "Press Space to Start."
		

		# MODO ESPERA INICIO
		waiting = 1
		while waiting:
			#get input
			for event in pygame.event.get():
				if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
					pygame.quit()
					sys.exit()
					online = 0
					print "exit"
				elif event.type == pygame.VIDEORESIZE:
					self.resize(event.size)
			keystate = pygame.key.get_pressed()
			if keystate[K_SPACE] or True:
				waiting = 0

			# clear/erase the last drawn sprites
			self.all.clear(self.screen, self.background)
			
			#draw the scene
			dirty = self.all.draw(self.screen)
			pygame.display.update(dirty)

			# cap the framerate
			self.clock.tick(FRAMERATE*self.SIMSPEED)
		
		print 20*"-"+"STARTED"+20*"-"
		self.robot_sim_clock.start()
		self.robot_imagination_clock.start()
		# MODO ONLINE
		key_released=True

		online = 1
		while online:
			for event in pygame.event.get():
				if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
					pygame.quit()
					sys.exit()
					online = 0
					print "exit"
				elif event.type == pygame.VIDEORESIZE:
					self.resize(event.size)
					
				if event.type == MOUSEBUTTONDOWN:
					if event.button == 1:
						ipos=pygame.mouse.get_pos()
						screen_size=self.screen.get_size()
						pos=Position((ipos[0]+0.)/self.ZOOM/PIXEL2M,(screen_size[1]-ipos[1]+0.0)/self.ZOOM/PIXEL2M)
						print ipos,pos
						self.robot[0].targets.append([self.robot[0].go_to_point,pos])
				elif event.type == KEYDOWN and key_released:
					key_released=False
					keystate = pygame.key.get_pressed()
					
					#control robot
					if keystate[K_UP]:
						self.robot[0].v+=0.3
					if keystate[K_DOWN]:
						self.robot[0].v-=0.3
						if self.robot[0].v<0.0:
							self.robot[0].v=0.0
					if keystate[K_LEFT]:
						self.robot[0].w+=0.1
					if keystate[K_RIGHT]:
						self.robot[0].w-=0.1
						
					if keystate[K_a]:
						self.robot[0].forward(0.1)
					if keystate[K_z]:
						self.robot[0].backward(0.1)
					if keystate[K_n]:
						self.robot[0].left(pi/16.0)
					if keystate[K_m]:
						self.robot[0].right(pi/16.0)
						
					#change floor
					if keystate[K_BACKSLASH]:
						self.changeVisibleFloor(newActiveFloor=-1)
					if keystate[K_0] or keystate[K_KP0]:
						self.changeVisibleFloor(newActiveFloor=0)
					if keystate[K_1] or keystate[K_KP1]:
						self.changeVisibleFloor(newActiveFloor=1)
					if keystate[K_2] or keystate[K_KP2]:
						self.changeVisibleFloor(newActiveFloor=2)
					if keystate[K_3] or keystate[K_KP3]:
						self.changeVisibleFloor(newActiveFloor=3)
						
					#change zoom
					if keystate[K_f]:
						pygame.display.toggle_fullscreen()
					if keystate[K_MINUS] or keystate[K_KP_MINUS]:
						if self.ZOOM>0.5:
							self.ZOOM/=1.2
						self.resize(self.zoomed())
					if keystate[K_PLUS] or keystate[K_KP_PLUS]:
						self.ZOOM*=1.2
						self.resize(self.zoomed())
						
					
						
				elif event.type == KEYUP:
					key_released=True
			
			self.building.update()
			for robot in self.robots:
				robot.draw_update()
				
			
			#~ self.simspeeds[0].update(self.SIMSPEED)
			
			# clear/erase the last drawn sprites #draw the scene
			self.all.clear(self.screen, self.background)
			dirty = self.all.draw(self.screen)
			pygame.display.update(dirty)
			

			# cap the framerate
			self.clock.tick(FRAMERATE)#*self.SIMSPEED)
		pygame.display.quit()
		quit()

if __name__ == '__main__':
	time0=time.time()
	sh = InteractiveConsole()
	sh.push("import readline, rlcompleter")
	sh.push("readline.parse_and_bind('tab: complete')")
	cls()
	print "\n"+47*"-"
	sh.push("from cleanSim import *")
	sh.push("c=CleanSim()")

	print "Inicializations are OK. Init time = " + str(round(time.time()-time0,2)) + "s"
	sh.push("c.start()")
	print 47*"-"
	
	sh.interact()
	
